﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using LevelEditor.Document;

namespace LevelEditor.Forms
{
    public partial class PaletteControl : Control
    {
        #region Properties

        /// <summary>
        /// Grid visibility. True = visible.
        /// </summary>
        [Category("Appearance")]
        [Description("Grid is visible")]
        [DefaultValue(true)]
        public bool GridVisible { get; set; }

        /// <summary>
        /// Thickness of the grid lines.
        /// </summary>
        [Category("Appearance")]
        [Description("Thickness of the grid lines.")]
        [DefaultValue(1)]
        public decimal GridThickness { get; set; }

        /// <summary>
        /// Size of a tile in pixels.
        /// </summary>
        [Category("Palette")]
        [Description("Size of a tile in pixels.")]
        [DefaultValue(typeof(Size), "16,16")]
        public Size TileSize { get; set; }

        /// <summary>
        /// Size of palette.
        /// </summary>
        [Category("Palette")]
        [Description("Size of palette in tiles.")]
        [DefaultValue(typeof(Size), "32,32")]
        public Size PaletteSize { get; set; }

        [Category("Palette")]
        [Description("Selected palette tiles.")]
        [DefaultValue(typeof(Rectangle), "0,0,0,0")]
        public Rectangle SelectedTiles { get; set; }

        /// <summary>
        /// Palette image.
        /// </summary>
        [Browsable(false)]
        public Bitmap Bitmap { set; get; }

        #endregion

        #region Constructors

        public PaletteControl()
        {
            InitializeComponent();

            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);

            GridVisible = true;
            GridThickness = 1;
            Bitmap = null;
            TileSize = new Size(16, 16);
            PaletteSize = new Size(32, 32);
        }

        #endregion

        #region Destructor

        ~PaletteControl()
        {
            if (Bitmap != null)
                Bitmap.Dispose();
        }

        #endregion

        #region Methods

        public void Reset(TileSet tileSet)
        {
            if (Bitmap != null)
                Bitmap.Dispose();
            if (tileSet.Image == null) tileSet.LoadImage();
            Bitmap = new Bitmap(tileSet.Image);
            TileSize = tileSet.TileSize;
            SelectedTiles = Rectangle.Empty;
            Size paletteSize = new Size(Bitmap.Width / TileSize.Width, Bitmap.Height / TileSize.Height);
            if (Bitmap.Width % TileSize.Width != 0)
                ++paletteSize.Width;
            if (Bitmap.Height % TileSize.Height != 0)
                ++paletteSize.Height;
            PaletteSize = paletteSize;
            Size = new Size(PaletteSize.Width * TileSize.Width + 1,
                PaletteSize.Height * TileSize.Height + 1);
            Invalidate();
        }

        public uint GetValueFromTilePoint(Point tile)
        {
            return (uint)((tile.Y * PaletteSize.Width) + tile.X);
        }

        public Point GetTilePointFromValue(uint value)
        {
            return new Point((int)value % PaletteSize.Width, (int)value / PaletteSize.Width);
        }

        #endregion

        #region Events

        private void PaletteControl_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.Clear(BackColor);
            if (Bitmap == null) return;
            g.Transform.Reset();
            g.DrawImageUnscaled(Bitmap, Point.Empty);

            // draw the grid
            if (GridVisible)
            {
                Pen gridPen = new Pen(ForeColor, Convert.ToSingle(GridThickness));
                for (int x = 0; x <= PaletteSize.Width; ++x)
                {
                    g.DrawLine(gridPen, new Point(x * TileSize.Width, 0),
                        new Point(x * TileSize.Width, TileSize.Height * PaletteSize.Height));
                }
                for (int y = 0; y <= PaletteSize.Height; ++y)
                {
                    g.DrawLine(gridPen, new Point(0, y * TileSize.Height),
                        new Point(TileSize.Width * PaletteSize.Width, y * TileSize.Height));
                }
                gridPen.Dispose();
            }

            // draw selection box
            if (SelectedTiles != Rectangle.Empty)
            {
                Rectangle selectionBox = Rectangle.Empty;
                selectionBox.X = SelectedTiles.X * TileSize.Width;
                selectionBox.Y = SelectedTiles.Y * TileSize.Height;
                selectionBox.Width = SelectedTiles.Width * TileSize.Width;
                selectionBox.Height = SelectedTiles.Height * TileSize.Height;
                Pen selectionPen = new Pen(Color.Magenta, 5f);
                g.DrawRectangle(selectionPen, selectionBox);

                selectionPen.Dispose();
            }
        }

        private Point GetTileFromMousePoint(Point mouse)
        {
            mouse.X /= TileSize.Width;
            mouse.Y /= TileSize.Height;
            return mouse;
        }

        private void PaletteControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Rectangle newSelection = Rectangle.Empty;
                newSelection.Width = newSelection.Height = 1;
                newSelection.Location = GetTileFromMousePoint(e.Location);
                if (newSelection.X >= PaletteSize.Width)
                    newSelection.X = PaletteSize.Width - 1;
                if (newSelection.Y >= PaletteSize.Height)
                    newSelection.Y = PaletteSize.Height - 1;
                SelectedTiles = newSelection;
                Invalidate();
            }
        }

        private void PaletteControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Rectangle newSelection = SelectedTiles;
                Point current = GetTileFromMousePoint(e.Location);
                newSelection.Width = current.X - newSelection.X + 1;
                newSelection.Height = current.Y - newSelection.Y + 1;
                if (newSelection.Width + newSelection.X >= PaletteSize.Width)
                    newSelection.Width = PaletteSize.Width - newSelection.X;
                if (newSelection.Height + newSelection.Y >= PaletteSize.Height)
                    newSelection.Height = PaletteSize.Height - newSelection.Y;
                SelectedTiles = newSelection;
                Invalidate();
            }
        }
        #endregion
    }
}
